import "./kendo.resizable.js";
import "./kendo.icons.js";

export const __meta__ = {
    id: "splitter",
    name: "Splitter",
    category: "web",
    description: "The Splitter widget provides an easy way to create a dynamic layout of resizable and collapsible panes.",
    depends: ["resizable", "icons"]
};

(function($, undefined) {
    var kendo = window.kendo,
        ui = kendo.ui,
        keys = kendo.keys,
        extend = $.extend,
        Widget = ui.Widget,
        pxUnitsRegex = /^\d+(\.\d+)?px$/i,
        percentageUnitsRegex = /^\d+(\.\d+)?%$/i,
        NS = ".kendoSplitter",
        EXPAND = "expand",
        COLLAPSE = "collapse",
        CONTENTLOAD = "contentLoad",
        ERROR = "error",
        RESIZE = "resize",
        RESIZING = "resizing",
        LAYOUTCHANGE = "layoutChange",
        HORIZONTAL = "horizontal",
        VERTICAL = "vertical",
        MOUSEENTER = "mouseenter",
        CLICK = "click",
        PANE = "pane",
        MOUSELEAVE = "mouseleave",
        FOCUSED = "k-focus",
        KPANE = "k-" + PANE,
        PANECLASS = "." + KPANE,
        KSCROLLABLE = "k-scrollable",
        TABINDEX = "tabindex",
        ARIA_VALUEMIN = "aria-valuemin",
        ARIA_VALUEMAX = "aria-valuemax",
        ARIA_VALUENOW = "aria-valuenow",
        ARIA_CONTROLS = "aria-controls",
        ARIA_LABEL = "aria-label",
        ARIA_LABELLEDBY = "aria-labelledby",
        ARIA_ORIENTATION = "aria-orientation",
        KSTATIC_PANE = "k-pane-static",
        SPLITTER = "k-splitter",
        KSPLITBAR = "k-splitbar",
        SPLITTER_FLEX = "k-splitter-flex",
        PANE_SIZING_PROP = "flex-basis",
        HORIZONTAL = "horizontal",
        VERTICAL = "vertical",
        KHIDDEN = "k-hidden",
        MAX_NUMBER_VALUE = Number.MAX_SAFE_INTEGER,
        KPANE = "k-pane",
        KPANE_FLEX = "k-pane-flex",
        CLICK = "click",
        RESIZE = "resize",
        PX = "px";

    function isPercentageSize(size) {
        return percentageUnitsRegex.test(size);
    }

    function isPixelSize(size) {
        return pxUnitsRegex.test(size) || /^\d+$/.test(size);
    }

    function isFluid(size) {
        return !isPercentageSize(size) && !isPixelSize(size);
    }

    function calculateSize(size, total) {
        var output = parseInt(size, 10);

        if (isPercentageSize(size)) {
            output = Math.floor(output * total / 100);
        }

        return output;
    }

    function panePropertyAccessor(propertyName, triggersResize) {
        return function(pane, value) {
            var paneConfig = this.element.find(pane).data(PANE);

            if (arguments.length == 1) {
                return paneConfig[propertyName];
            }

            ((this.options.panes || []).find(p => p.uid == paneConfig.uid) || {})[propertyName] = value;
            paneConfig[propertyName] = value;
            paneConfig.isFluid = isFluid(paneConfig.size);

            if (triggersResize) {
                var splitter = this.element.data("kendo" + this.options.name);
                splitter.resize(true);
            }
        };
    }

    var Splitter = Widget.extend({
        init: function(element, options) {
            var that = this,
                isHorizontal;

            Widget.fn.init.call(that, element, options);

            that.wrapper = that.element;

            if (that.options.orientation) {
                isHorizontal = that.options.orientation.toLowerCase() != VERTICAL;
            }

            that.orientation = isHorizontal ? HORIZONTAL : VERTICAL;
            that._dimension = isHorizontal ? "width" : "height";
            that._keys = {
                decrease: isHorizontal ? keys.LEFT : keys.UP,
                increase: isHorizontal ? keys.RIGHT : keys.DOWN
            };

            that._resizeStep = 10;

            that._marker = kendo.guid().substring(0, 8);

            that.element.addClass(`${SPLITTER} ${SPLITTER_FLEX} ${SPLITTER}-${that.orientation}`);
            that.element.closest(KPANE).removeClass(KSTATIC_PANE).addClass(KPANE_FLEX);
            that._initPanes();

            that.resizing = new PaneResizing(that);

            that.element.triggerHandler("init" + NS);
        },
        events: [
            EXPAND,
            COLLAPSE,
            CONTENTLOAD,
            ERROR,
            RESIZE,
            RESIZING,
            LAYOUTCHANGE
        ],

        _addOverlays: function() {
            this._panes().append("<div class='k-splitter-overlay k-overlay' />");
        },

        _removeOverlays: function() {
            this._panes().children(".k-splitter-overlay").remove();
        },

        _attachEvents: function() {
            var that = this,
                orientation = that.options.orientation;

            // do not use delegated events to increase performance of nested elements
            that.element
                .children(".k-splitbar-draggable-" + orientation)
                .on("keydown" + NS, that._keydown.bind(that))
                .on("mousedown" + NS, function(e) { e.currentTarget.focus({ preventScroll: true }); })
                .on("focus" + NS, function(e) { $(e.currentTarget).addClass(FOCUSED); })
                .on("blur" + NS, function(e) {
                    $(e.currentTarget).removeClass(FOCUSED);
                    if (that.resizing) {
                        that.resizing.end();
                    }
                })
                .on(MOUSEENTER + NS, function() { $(this).addClass("k-splitbar-" + that.orientation + "-hover"); })
                .on(MOUSELEAVE + NS, function() { $(this).removeClass("k-splitbar-" + that.orientation + "-hover"); })
                .on("mousedown" + NS, that._addOverlays.bind(that))
                .end()
                .children(".k-splitbar")
                .on("dblclick" + NS, that._togglePane.bind(that))
                .children(".k-collapse-next, .k-collapse-prev").on(CLICK + NS, that._arrowClick(COLLAPSE)).end()
                .children(".k-expand-next, .k-expand-prev").on(CLICK + NS, that._arrowClick(EXPAND)).end()
                .end();

            $(window).on("resize" + NS + that._marker, that.resize.bind(that, false));
            $(document).on("mouseup" + NS + that._marker, that._removeOverlays.bind(that));
        },

        _detachEvents: function() {
            var that = this;

            that.element
                .children(".k-splitbar-draggable-" + that.orientation).off(NS).end()
                .children(".k-splitbar").off("dblclick" + NS)
                .children(".k-collapse-next, .k-collapse-prev, .k-expand-next, .k-expand-prev").off(NS);

            $(window).off(NS + that._marker);
            $(document).off(NS + that._marker);
        },

        options: {
            name: "Splitter",
            clickMoveClick: true,
            orientation: HORIZONTAL,
            panes: []
        },

        destroy: function() {
            Widget.fn.destroy.call(this);

            this._detachEvents();

            if (this.resizing) {
                this.resizing.destroy();
            }

            kendo.destroy(this.element);

            this.wrapper = this.element = null;
        },

        _keydown: function(e) {
            var that = this,
                key = e.keyCode,
                resizing = that.resizing,
                target = $(e.currentTarget),
                navigationKeys = that._keys,
                increase = key === navigationKeys.increase,
                decrease = key === navigationKeys.decrease,
                pane;

            if (increase || decrease) {
                if (e.ctrlKey) {
                    pane = target[decrease ? "next" : "prev"]();

                    if (resizing && resizing.isResizing()) {
                        resizing.end();
                    }

                    if (!pane[that._dimension]()) {
                        that._triggerAction(EXPAND, pane);
                    } else {
                        that._triggerAction(COLLAPSE, target[decrease ? "prev" : "next"]());
                    }
                } else if (resizing) {
                    resizing.move((decrease ? -1 : 1) * that._resizeStep, target);
                }
                e.preventDefault();
            } else if (key === keys.HOME) {
                pane = target.prev();
                that.collapse(pane);
                e.preventDefault();
            } else if (key === keys.END) {
                pane = target.prev();
                that.expand(pane);
                e.preventDefault();
            } else if (key === keys.ENTER && resizing) {
                resizing.end();
                e.preventDefault();

                that._togglePane(e);
            }
        },

        _initPanes: function() {
            var panesConfig = this.options.panes || [];
            var that = this;

            this.element
                .children()
                .each(function(i, pane) {
                    if (pane.nodeName.toLowerCase() != "script") {
                        panesConfig[i] = $.extend(that._getDefaultPaneConfig(), panesConfig[i], { order: i * 2 });
                        panesConfig[i].isFluid = isFluid(panesConfig[i].size);
                        pane.style.order = i * 2;

                        that._initPane(pane, panesConfig[i]);
                    }
                });

            this.resize();
        },
        _getDefaultPaneConfig: function() {
            return { scrollable: true, resizable: true, size: "auto", uid: kendo.guid() };
        },
        _updatePaneOrderStyles: function(parentElement) {
            $(parentElement || this.element).children().each(function(i, pane) {
                if (pane.nodeName.toLowerCase() != "script") {
                    let paneConfig = pane.data(PANE);

                    paneConfig.order = i * 2;
                    pane.style.order = i * 2;
                }
            });
        },
        _initPane: function(pane, config) {
            config = $.extend({}, this._getDefaultPaneConfig(), config);
            config.fixedSize = config.size && config.size !== "auto";
            pane = $(pane)
                .attr("role", "group")
                .attr("data-uid", config.uid)
                .addClass(KPANE);

            let isStaticPane = !config.resizable && !config.collapsible || config.fixedSize;

            pane.css(PANE_SIZING_PROP, config.size)
                .data(PANE, config)
                .toggleClass(KSTATIC_PANE, Boolean(isStaticPane))
                .toggleClass(KSCROLLABLE, Boolean(config.scrollable));

            this.ajaxRequest(pane);
        },

        ajaxRequest: function(pane, url, data) {
            var that = this,
                paneConfig;

            pane = that.element.find(pane);
            paneConfig = pane.data(PANE);

            url = url || paneConfig.contentUrl;

            if (url) {
                pane.append("<span class='k-icon k-i-loading k-pane-loading' />");

                if (kendo.isLocalUrl(url)) {
                    jQuery.ajax({
                        url: url,
                        data: data || {},
                        type: "GET",
                        dataType: "html",
                        success: function(data) {
                            pane.html(data);

                            that.trigger(CONTENTLOAD, { pane: pane[0] });
                        },
                        error: function(xhr, status) {
                            that.trigger(ERROR, {
                                pane: pane[0],
                                status: status,
                                xhr: xhr
                            });
                        }
                    });
                } else {
                    pane.removeClass(KSCROLLABLE)
                        .html("<iframe src='" + url + "' frameborder='0' class='k-content-frame'>" +
                            "This page requires frames in order to show content" +
                            "</iframe>");
                }
            }
        },

        _triggerAction: function(type, pane) {
            var paneConfig = pane.data(PANE);
            var shouldExecute = (paneConfig.collapsed && type == EXPAND) || (!paneConfig.collapsed && type == COLLAPSE);

            if (!paneConfig.collapsible) {
                return;
            }

            if (shouldExecute && !this.trigger(type, { pane: pane[0] })) {
                this[type](pane[0]);
            }

            this.resizing.stop();
            this.resizing.end();
        },

        _togglePane: function(e) {
            var that = this,
                target = $(e.target),
                arrow;

            if (target.closest(".k-splitter")[0] != that.element[0]) {
                return;
            }

            arrow = target.children("span:not(.k-resize-handle)");

            if (arrow.length !== 1) {
                return;
            }

            if (arrow.is(".k-collapse-prev")) {
                that._triggerAction(COLLAPSE, target.prev());
            } else if (arrow.is(".k-collapse-next")) {
                that._triggerAction(COLLAPSE, target.next());
            } else if (arrow.is(".k-expand-prev")) {
                that._triggerAction(EXPAND, target.prev());
            } else if (arrow.is(".k-expand-next")) {
                that._triggerAction(EXPAND, target.next());
            }

            that.resizing?.end();
        },
        _arrowClick: function(arrowType) {
            var that = this;

            return function(e) {
                var target = $(e.currentTarget),
                    pane;

                if (target.closest(".k-splitter")[0] != that.element[0]) {
                    return;
                }

                if (target.is(".k-" + arrowType + "-prev")) {
                    pane = target.parent().prev();
                } else {
                    pane = target.parent().next();
                }
                that._triggerAction(arrowType, pane);
            };
        },
        _updatePaneOrders: function()
        {
            var that = this;
            var panes = that._getPaneElements();

            panes.forEach((pane, index) => {
                var paneConfig = $(pane).data(PANE);
                if (paneConfig) {
                    paneConfig.order = index * 2;
                    pane.style.order = index * 2;
                }
            });
        },
        _updateSplitBar: function(splitbar, previousPane, nextPane, previousPaneEl) {
            var catIconIf = function(actionType, iconType, condition) {
                var icon = iconType ? ui.icon({ icon: iconType, size: "xsmall" }) : "";
                return condition ? "<span class='k-" + actionType + "'>" + icon + "</span>" : "";
            },
                orientation = this.orientation,
                draggable = (previousPane.resizable !== false) && (nextPane.resizable !== false),
                prevCollapsible = previousPane.collapsible,
                prevCollapsed = previousPane.collapsed,
                nextCollapsible = nextPane.collapsible,
                nextCollapsed = nextPane.collapsed,
                previousPaneId = previousPaneEl.attr("id");

            if (!previousPaneId) {
                previousPaneId = kendo.guid();
                previousPaneEl.attr("id", previousPaneId);
            }

            const isRtl = kendo.support.isRtl(splitbar);
            const leftIcon = isRtl ? "caret-alt-right" : "caret-alt-left";
            const rightIcon = isRtl ? "caret-alt-left" : "caret-alt-right";

            splitbar.addClass("k-splitbar k-splitbar-" + orientation)
                .attr("role", "separator")
                .attr(ARIA_VALUEMIN, "0")
                .attr(ARIA_VALUEMAX, "100")
                .attr(ARIA_CONTROLS, previousPaneId)
                .removeClass("k-splitbar-" + orientation + "-hover")
                .toggleClass("k-splitbar-draggable-" + orientation,
                    draggable && !prevCollapsed && !nextCollapsed)
                .toggleClass("k-splitbar-static-" + orientation,
                    !draggable && !prevCollapsible && !nextCollapsible)
                .html(
                    catIconIf("collapse-prev", "caret-alt-up", prevCollapsible && !prevCollapsed && !nextCollapsed && orientation == VERTICAL) +
                    catIconIf("collapse-prev", leftIcon, prevCollapsible && !prevCollapsed && !nextCollapsed && orientation == HORIZONTAL) +
                    catIconIf("expand-prev", "caret-alt-down", prevCollapsible && prevCollapsed && !nextCollapsed && orientation == VERTICAL) +
                    catIconIf("expand-prev", rightIcon, prevCollapsible && prevCollapsed && !nextCollapsed && orientation == HORIZONTAL) +
                    catIconIf("resize-handle", null, draggable && orientation == VERTICAL) +
                    catIconIf("resize-handle", null, draggable && orientation == HORIZONTAL) +
                    catIconIf("collapse-next", "caret-alt-down", nextCollapsible && !nextCollapsed && !prevCollapsed && orientation == VERTICAL) +
                    catIconIf("collapse-next", rightIcon, nextCollapsible && !nextCollapsed && !prevCollapsed && orientation == HORIZONTAL) +
                    catIconIf("expand-next", "caret-alt-up", nextCollapsible && nextCollapsed && !prevCollapsed && orientation == VERTICAL) +
                    catIconIf("expand-next", leftIcon, nextCollapsible && nextCollapsed && !prevCollapsed && orientation == HORIZONTAL)
                );

            if (previousPane.labelId) {
                splitbar.attr(ARIA_LABELLEDBY, previousPane.labelId);
            } else if (previousPane.label) {
                splitbar.attr(ARIA_LABEL, previousPane.label);
            }

            if (orientation == HORIZONTAL) {
                splitbar.attr(ARIA_ORIENTATION, VERTICAL);
            }

            if (!draggable && !prevCollapsible && !nextCollapsible) {
                splitbar.removeAttr(TABINDEX);
            }
        },
        _updateSplitBars: function() {
            var that = this;
            this.element.children(".k-splitbar").each(function() {
                var splitbar = $(this),
                    previousPaneEl = splitbar.prevAll(PANECLASS).first(),
                    previousPane = previousPaneEl.data(PANE),
                    nextPane = splitbar.nextAll(PANECLASS).first().data(PANE);

                // TODO: check if the proper place to set order
                splitbar.css("order", previousPane.order + 1);

                if (!nextPane) {
                    return;
                }

                that._updateSplitBar(splitbar, previousPane, nextPane, previousPaneEl);
            });
        },
        _removeSplitBars: function() {
            this.element.children(".k-splitbar").remove();
        },
        _panes: function() {
            if (!this.element) {
                return $();
            }
            return this.element.children(PANECLASS);
        },

        _resetAriaValueNow: function(splitBars, panesSizes) {
            var i, splitbar, valueNow, joinDimension;

            for (i = 0; i < splitBars.length; i++) {
                joinDimension = (panesSizes[i] + panesSizes[i + 1]) || 1;
                valueNow = Math.round(panesSizes[i] / joinDimension * 100);
                splitbar = splitBars[i];
                splitbar.setAttribute(ARIA_VALUENOW, valueNow);
            }
        },

        _resize: function() {
            var that = this,
                element = that.element,
                panes = element.children(PANECLASS),
                isHorizontal = that.orientation == HORIZONTAL,
                splitBars = element.children(".k-splitbar"),
                splitBarsCount = splitBars.length,
                sizingProperty = isHorizontal ? "width" : "height",
                totalSize = element[sizingProperty](),
                panesSizes = [];

            that.wrapper.addClass("k-splitter-resizing");

            if (that._suppressResize) {
                return;
            }

            if (splitBarsCount === 0) {
                splitBarsCount = panes.length - 1;
                panes.slice(0, splitBarsCount)
                    .after("<div tabindex='0' class='k-splitbar' data-marker='" + that._marker + "' />");

                that._updateSplitBars();
                splitBars = element.children(".k-splitbar");
            } else {
                that._updateSplitBars();
            }

            // discard splitbar sizes from total size
            splitBars.each(function() {
                totalSize -= this[isHorizontal ? "offsetWidth" : "offsetHeight"];
            });

            var sizedPanesWidth = 0,
                sizedPanesCount = 0,
                freeSizedPanes = $();

            panes
                .each(function() {
                    var element = $(this),
                        config = element.data(PANE) || {}, size;

                    element.removeClass("k-collapsed");
                    if (config.collapsed) {
                        size = config.collapsedSize ? calculateSize(config.collapsedSize, totalSize) : 0;
                        element.css("overflow", "hidden").addClass("k-collapsed");
                    } else if (config.isFluid || isFluid(config.size)) {
                        freeSizedPanes = freeSizedPanes.add(this);
                        panesSizes.push(false);
                        return;
                    } else { // sized in px/%, not collapsed
                        size = calculateSize(config.size, totalSize);
                    }

                    sizedPanesCount++;
                    sizedPanesWidth += size;
                    panesSizes.push(size);
                    element.css(sizingProperty, "");
                    element.css(PANE_SIZING_PROP, size + PX);
                    return size;
                });

            totalSize -= sizedPanesWidth;

            var freeSizePanesCount = freeSizedPanes.length,
                freeSizePaneWidth = Math.floor(totalSize / freeSizePanesCount);

            freeSizedPanes
                .slice(0, freeSizePanesCount - 1)
                .css(PANE_SIZING_PROP, freeSizePaneWidth + PX)
                .end()
                .eq(freeSizePanesCount - 1)
                .css(PANE_SIZING_PROP, (totalSize - (freeSizePanesCount - 1) * freeSizePaneWidth) + PX);

            panesSizes.forEach(function(size, i) {
                if (size === false) {
                    panesSizes[i] = freeSizePaneWidth;
                }
            });

            that._resetAriaValueNow(splitBars, panesSizes);

            // arrange panes
            var sizingDomProperty = isHorizontal ? "offsetWidth" : "offsetHeight";

            if (freeSizePanesCount === 0) {
                var lastNonCollapsedPane = panes.filter(function() {
                    return !(($(this).data(PANE) || {}).collapsed);
                }).last();

                if (lastNonCollapsedPane.length) {
                    const expandTotal = (totalSize + lastNonCollapsedPane[0][sizingDomProperty]);
                    lastNonCollapsedPane.css(PANE_SIZING_PROP, expandTotal + PX);
                }
            }

            that._detachEvents();
            that._attachEvents();

            that.wrapper.removeClass("k-splitter-resizing");

            kendo.resize(panes);
            that.trigger(LAYOUTCHANGE);
        },
        toggle: function(pane, expand) {
            var that = this,
                paneConfig;

            pane = that.element.find(pane);
            paneConfig = pane.data(PANE);

            if (!expand && paneConfig?.collapsible !== true) {
                return;
            }

            if (arguments.length == 1) {
                expand = paneConfig.collapsed === undefined ? false : paneConfig.collapsed;
            }

            paneConfig.collapsed = !expand;

            pane.toggleClass(KHIDDEN, paneConfig.collapsed && !paneConfig.collapsedSize);
            pane.css("overflow", paneConfig.collapsed && !paneConfig.collapsedSize ? "hidden" : "auto");
            that.resize(true);
        },

        collapse: function(pane) {
            this.toggle(pane, false);
        },

        expand: function(pane) {
            this.toggle(pane, true);
        },

        _addPane: function(config, idx, paneElement) {
            const that = this;

            if (paneElement.length) {
                that.options.panes.splice(idx, 0, config);
                that._initPane(paneElement, config);

                that._removeSplitBars();
                that._updatePaneOrders();
                that.resize(true);
            }

            return paneElement;
        },

        append: function(config) {
            config = config || {};

            var that = this,
                paneElement = $("<div />").appendTo(that.element);

            return that._addPane(config, that.options.panes.length, paneElement);
        },

        insertBefore: function(config, referencePane) {
            referencePane = $(referencePane);
            config = config || {};

            var that = this,
                idx = that.wrapper.children(".k-pane").index(referencePane),
                paneElement = $("<div />").insertBefore($(referencePane));

            return that._addPane(config, idx, paneElement);
        },

        insertAfter: function(config, referencePane) {
            referencePane = $(referencePane);
            config = config || {};

            var that = this,
                idx = that.wrapper.children(".k-pane").index(referencePane),
                paneElement = $("<div />").insertAfter($(referencePane));

            return that._addPane(config, idx + 1, paneElement);
        },

        remove: function(pane) {
            var that = this;
            pane = that.wrapper.find(pane);

            if (pane.length) {
                kendo.destroy(pane);
                pane.each(function(idx, element) {
                    that.options.panes.splice(that.wrapper.children(".k-pane").index(element), 1);
                    $(element).remove();
                });

                that._removeSplitBars();

                if (that.options.panes.length) {
                    that.resize(true);
                }
            }

            return that;
        },

        size: panePropertyAccessor("size", true),

        min: panePropertyAccessor("min"),

        max: panePropertyAccessor("max"),

        _getPaneElement: function(paneIndex) {
            const that = this;
            const panes = that._getPaneElements();
            return panes[paneIndex];
        },

        _getPaneElements: function() {
            const that = this;
            const panes = Array.from(that.element.children() || []).filter(x => $(x).hasClass("k-pane") || $(x).hasClass("k-splitter"));
            return panes;
        },
        _dragSplitterBar: function(splitterBarIndex, delta) {
            const that = this;
            const { leftPane, rightPane } = that._getAdjacentPanes(splitterBarIndex);

            const leftPaneNewSize = leftPane.computedSize + delta;
            const isLeftPaneSizeInBounds = leftPaneNewSize > leftPane.min && leftPaneNewSize < leftPane.max;

            const panesWithoutSize = that._getPaneElements().filter(x => !x.style[PANE_SIZING_PROP]);
            const canResizeBothPanes = (leftPane.size || rightPane.size) && panesWithoutSize.length > 1;

            if ((leftPane.size && rightPane.size) || canResizeBothPanes) {
                if (isLeftPaneSizeInBounds) {
                    that._resizePane(leftPane, delta);
                    that._resizePane(rightPane, -delta);
                }
            } else if (rightPane.size) {
                that._resizePane(rightPane, -delta);
            } else {
                that._resizePane(leftPane, delta);
            }
            return { leftPane, rightPane };
        },
        _getAdjacentPanes: function(splitterBarIndex) {
            const that = this;
            const leftPaneIndex = splitterBarIndex;
            const rightPaneIndex = splitterBarIndex + 1;

            const leftPaneELement = that._getPaneElement(leftPaneIndex);
            const rightPaneELement = that._getPaneElement(rightPaneIndex);

            const leftPane = that._getPane(leftPaneIndex);
            const rightPane = that._getPane(rightPaneIndex);

            const leftPaneSize = that._getPaneOffsetSize(leftPaneIndex);
            const rightPaneSize = that._getPaneOffsetSize(rightPaneIndex);

            const totalPaneSize = leftPaneSize + rightPaneSize;
            const splitterSize = that._getElementClientSize(that.element, that.options.orientation);
            const getPixelSize = paneSize => that._calculatePixelSize(paneSize, splitterSize);

            const { leftPaneMaxSize, rightPaneMaxSize } = that._getAdjacentPanesMaxSize(leftPaneIndex, rightPaneIndex);
            const rightMaxPixelSize = getPixelSize(rightPane && rightPane.max);
            const leftMaxPixelSize = getPixelSize(leftPane && leftPane.max);

            return {
                leftPane: {
                    index: leftPaneIndex,
                    computedSize: leftPaneSize,
                    min: getPixelSize(leftPane && leftPane.min) || (rightMaxPixelSize ? totalPaneSize - rightMaxPixelSize : 0) || 0,
                    max: leftPaneMaxSize,
                    size: leftPaneELement.style[PANE_SIZING_PROP],
                    collapsible: leftPane && leftPane.collapsible,
                    uid: leftPane.uid
                },
                rightPane: {
                    index: rightPaneIndex,
                    computedSize: rightPaneSize,
                    min: getPixelSize(rightPane && rightPane.min) || (leftMaxPixelSize ? totalPaneSize - leftMaxPixelSize : 0) || 0,
                    max: rightPaneMaxSize,
                    size: rightPaneELement.style[PANE_SIZING_PROP],
                    collapsible: rightPane && rightPane.collapsible,
                    uid: rightPane.uid
                }
            };
        },

        _resizePane: function(pane, delta) {
            const that = this;
            const constrainedSize = clamp(pane.computedSize + delta, pane.min, pane.max);
            let newSize = "";

            if (isPercentageSize(pane.size)) {
                const splitterSize = that._getElementClientSize(that.element, that.options.orientation);
                newSize = toPercentages(100 * constrainedSize / splitterSize);
            } else {
                newSize = toPixel(constrainedSize);
            }
            pane.size = newSize;
            pane.isFluid = isFluid(newSize);
            that._setPaneSize(pane.index, newSize);
        },

        _allExpandedPanesHaveSize: function() {
            const that = this;
            const expandedPanes = that.options.panes.filter(x => !x.collapsed);

            if (expandedPanes.length) {
                return expandedPanes.filter(x => x.size).length;
            }

            return false;
        },

        _setPaneSize: function(paneIndex, size) {
            const that = this;
            const paneElement = that._getPaneElement(paneIndex);

            if (!paneElement) {
                return;
            }

            if (!that._allExpandedPanesHaveSize()) {
                $(paneElement).addClass(KSTATIC_PANE);
            }

            paneElement.style[PANE_SIZING_PROP] = size;
            let paneConfig = $(paneElement).data("pane");
            paneConfig.size = size;
            paneConfig.isFluid = isFluid(size);

            that.trigger(RESIZING, { pane: paneElement });
        },

        _getPaneSizes: function(paneIndex) {
            const that = this;
            const splitterSize = that._getElementClientSize(that.element, that.options.orientation);
            const pane = that._getPane(paneIndex);
            const paneSize = that._getPaneOffsetSize(paneIndex);
            const paneMinSize = pane && pane.min ? that._calculatePixelSize(pane.min, splitterSize) : 0;
            const paneMaxSize = pane && pane.max ? that._calculatePixelSize(pane.max, splitterSize) : MAX_NUMBER_VALUE;

            return {
                size: paneSize,
                min: paneMinSize,
                max: paneMaxSize
            };
        },

        _calculatePixelSize: function(size, containerSize) {
            let numericSize = kendo.parseFloat(size);

            if (isPercentageSize(size)) {
                numericSize = (containerSize * numericSize);
            }

            return numericSize;
        },

        _getPaneOffsetSize: function(paneIndex) {
            const that = this;
            const paneElement = that._getPaneElement(paneIndex);
            const size = that._getElementOffsetSize(paneElement, that.options.orientation);
            return size;
        },


        _getElementOffsetSize: function(element, orientation) {
            if (!element) {
                return 0;
            }

            const rect = element.getBoundingClientRect();

            if (orientation === HORIZONTAL) {
                return rect.width;
            } else {
                return rect.height;
            }
        },

        _getElementClientSize: function(element, orientation) {
            const that = this;

            return that._getElementSize(element, orientation, "client");
        },

        _getElementSize: function(element, orientation, sizeType) {
            if (!element) {
                return 0;
            }

            element = element[0];

            if (orientation === HORIZONTAL) {
                return element[`${sizeType}Width`];
            } else {
                return element[`${sizeType}Height`];
            }
        },

        _getPane: function(paneIndex) {
            const that = this;

            return (that.options.panes || [])[paneIndex];
        },

        _getPaneIndex: function(pane) {
            const that = this;

            return that.options.panes.indexOf(pane);
        },

        _getAdjacentPanesMaxSize: function(leftPaneIndex, rightPaneIndex) {
            const that = this;
            const {
                size: leftPaneSize,
                min: leftPaneMinSize,
                max: leftPaneMaxPixelSize
            } = that._getPaneSizes(leftPaneIndex);

            const {
                size: rightPaneSize,
                min: rightPaneMinSize,
                max: rightPaneMaxPixelSize
            } = that._getPaneSizes(rightPaneIndex);

            const totalPaneSize = leftPaneSize + rightPaneSize;

            const leftPaneMaxSize = Math.min(leftPaneMaxPixelSize, totalPaneSize - rightPaneMinSize);
            const rightPaneMaxSize = Math.min(rightPaneMaxPixelSize, totalPaneSize - leftPaneMinSize);

            return {
                leftPaneMaxSize,
                rightPaneMaxSize
            };
        },
        _getElementIndex: function(element, childrenSelector) {
            if (!element) {
                return [].indexOf(element);
            }

            let children = Array.from(element.parent().children());

            if (childrenSelector) {
                children = children.filter(x => x.matches(childrenSelector));
            }

            return Array.from(children).indexOf(element[0]);
        },
    });

    ui.plugin(Splitter);

    function toPercentages(value) {
        return `${value}%`;
    }

    function toPixel(value) {
        return kendo.parseFloat(value) + "px";
    }

    function percentage(partialValue, totalValue) {
        return (100 * partialValue) / totalValue;
    }

    function clamp(value, min, max) {
        return Math.min(max, Math.max(min, value));
    }

    var verticalDefaults = {
        sizingProperty: "height",
        sizingDomProperty: "offsetHeight",
        alternateSizingProperty: "width",
        positioningProperty: "top",
        mousePositioningProperty: "pageY"
    };

    var horizontalDefaults = {
        sizingProperty: "width",
        sizingDomProperty: "offsetWidth",
        alternateSizingProperty: "height",
        positioningProperty: "left",
        mousePositioningProperty: "pageX"
    };

    function PaneResizing(splitter) {
        var that = this,
            orientation = splitter.orientation,
            handle = ".k-splitbar-draggable-" + orientation + "[data-marker=" + splitter._marker + "]";

        if (splitter.options.clickMoveClick) {
            handle += ",.k-ghost-splitbar";
        }

        that.owner = splitter;
        that._element = splitter.element;
        that.orientation = orientation;

        extend(that, orientation === HORIZONTAL ? horizontalDefaults : verticalDefaults);

        that._resizable = new kendo.ui.Resizable(splitter.element, {
            orientation: orientation,
            handle: handle,
            clickMoveClick: splitter.options.clickMoveClick,
            hint: that._createHint.bind(that),
            start: that._start.bind(that),
            max: that._max.bind(that),
            min: that._min.bind(that),
            invalidClass: "k-restricted-size-" + orientation,
            resize: that._resize.bind(that),
            resizeend: that._stop.bind(that)
        });
    }

    PaneResizing.prototype = {
        stop: function() {
            this._resizable._stop();
        },

        press: function(target) {
            this._resizable.press(target);
            this.pressed = true;
        },

        move: function(delta, target) {
            if (!target.hasClass("k-splitbar-draggable-horizontal") && !target.hasClass("k-splitbar-draggable-vertical")) {
                return;
            }

            const splitterBarIndex = this.owner._getElementIndex(target, `.${KSPLITBAR}`);
            const { leftPane, rightPane } = this.owner._dragSplitterBar(splitterBarIndex, delta);
            this.owner.trigger(RESIZE, { leftPane: leftPane, rightPane: rightPane });
        },

        end: function() {
            this._resizable.end();
            this.pressed = false;
        },

        destroy: function() {
            this._resizable.destroy();
            this._resizable = this._element = this.owner = null;
        },

        isResizing: function() {
            return this._resizable.resizing;
        },

        _createHint: function(handle) {
            var that = this;
            return $("<div class='k-ghost-splitbar k-ghost-splitbar-" + that.orientation + "' />")
                .css("z-index", 99)
                .css(that.alternateSizingProperty, handle[that.alternateSizingProperty]());
        },

        _start: function(e) {
            var that = this,
                splitbar = $(e.currentTarget);

            const isRtl = kendo.support.isRtl(that._element);
            let offsetBoundaryProp = that.orientation === HORIZONTAL ? "offsetLeft" : "offsetTop";
            const splitterBarIndex = that.owner._getElementIndex(splitbar, `.${KSPLITBAR}`);

            const leftPaneELement = that.owner._getPaneElement(splitterBarIndex);
            const rightPaneELement = that.owner._getPaneElement(splitterBarIndex + 1);
            let previousPane = $((that.orientation === HORIZONTAL && isRtl) ? rightPaneELement : leftPaneELement);
            let nextPane = $((that.orientation === HORIZONTAL && isRtl) ? leftPaneELement : rightPaneELement);

            if ($(e.initialTarget).closest(".k-expand-next, .k-expand-prev, .k-collapse-next, .k-collapse-prev").length > 0 ||
                !nextPane.length ||
                !previousPane.length) {
                e.preventDefault();
                return;
            }

            var previousPaneConfig = previousPane.data(PANE),
                nextPaneConfig = nextPane.data(PANE),
                prevBoundary = parseInt(previousPane[0][offsetBoundaryProp], 10),
                nextBoundary = parseInt(nextPane[0][offsetBoundaryProp], 10) + nextPane[0][that.sizingDomProperty] - splitbar[0][that.sizingDomProperty],
                totalSize = parseInt(that._element.css(that.sizingProperty), 10),
                toPx = function(value) {
                    var val = parseInt(value, 10);
                    return (isPixelSize(value) ? val : (totalSize * val) / 100) || 0;
                };

            if (!previousPaneConfig || !nextPaneConfig) {
                e.preventDefault();
                e.sender.draggable.clickMoveClick.cancel();
                that.owner.element.find(".k-ghost-splitbar").remove();
                return;
            }

            var prevMinSize = toPx(previousPaneConfig.min),
                prevMaxSize = toPx(previousPaneConfig.max) || nextBoundary - prevBoundary,
                nextMinSize = toPx(nextPaneConfig.min),
                nextMaxSize = toPx(nextPaneConfig.max) || nextBoundary - prevBoundary;

            that.previousPane = previousPane;
            that.nextPane = nextPane;
            that._maxPosition = Math.min(nextBoundary - nextMinSize, prevBoundary + prevMaxSize);
            that._minPosition = Math.max(prevBoundary + prevMinSize, nextBoundary - nextMaxSize);
        },
        _max: function() {
            return this._maxPosition;
        },
        _min: function() {
            return this._minPosition;
        },
        _resize: function(e) {
            let that = this;
            let splitter = that.owner;
            let orientation = splitter.orientation;
            let delta;
            let owner = this.owner;
            const splitterBar = e.currentTarget || e.target;

            if (!splitterBar) {
                return;
            }

            const splitterBarIndex = splitter._getElementIndex(splitterBar, `.${KSPLITBAR}`);
            const rtlModifier = kendo.support.isRtl(that._element) ? -1 : 1;
            if (orientation === HORIZONTAL) {
                delta = e.x.delta * rtlModifier;
            } else {
                delta = e.y.delta;
            }

            let splitbarPosition = orientation === HORIZONTAL ? splitterBar.position().left : splitterBar.position().top;
            let ghostPosition = e.position;

            if (Math.abs(splitbarPosition - ghostPosition) > 2) {
                owner._dragSplitterBar(splitterBarIndex, delta);
            }
        },
        _stop: function(e) {
            var that = this,
                splitbar = $(e.currentTarget),
                owner = that.owner;
            let isRtl = kendo.support.isRtl(that._element);
            owner._panes().children(".k-splitter-overlay").remove();

            if (e.keyCode !== kendo.keys.ESC) {
                let delta = owner.orientation === HORIZONTAL ? e.x.initialDelta : e.y.initialDelta;
                let splitbarPosition = owner.orientation === HORIZONTAL ? splitbar.position().left : splitbar.position().top;
                let ghostPosition = e.position;
                let rtlModifier = (owner.orientation === HORIZONTAL && isRtl) ? -1 : 1;

                const splitterBarIndex = this.owner._getElementIndex(e.currentTarget, `.${KSPLITBAR}`);
                if (Math.abs(splitbarPosition - ghostPosition) > 2) {
                    owner._dragSplitterBar(splitterBarIndex, delta * rtlModifier);
                }

                const { leftPane, rightPane } = owner._getAdjacentPanes(splitterBarIndex);
                owner.trigger(RESIZE, { leftPane: leftPane, rightPane: rightPane });
            }

            return false;
        }
    };

})(window.kendo.jQuery);
export default kendo;

